﻿using System;
using System.Xml.Linq;
using Net.Chirash.Twitter.TwitterDatabase;

namespace Net.Chirash.Twitter.TwitterClient
{
    public class TwitterCommand : ITwitterCommand
    {
        #region Public Properties

        public TwitterConnection Connection { get; set; }
        public User CurrentUser { get; private set; }

        #endregion

        #region Constructors

        public TwitterCommand()
        {
            apiUri = new Uri(string.Format("http://api.twitter.com/{0}/", (int)version));
            streamUri = new Uri(string.Format("https://userstream.twitter.com/{0}/", (int)streamVersion));
        }

        public TwitterCommand(TwitterConnection connection)
            : this()
        {
            this.Connection = connection;
        }

        public TwitterCommand(string consumerKey, string consumerSecret, string token, string tokenSecret)
            : this()
        {
            this.Connection = new TwitterConnection()
                   {
                       ConsumerKey = consumerKey,
                       ConsumerSecret = consumerSecret,
                       Token = token,
                       TokenSecret = tokenSecret
                   };
        }

        #endregion

        #region Timeline

        protected Timeline GetTimeline(string name, TimelineOption option)
        {
            Uri uri = new Uri(apiUri, "statuses/" + name);
            string url = uri.ToString() + option.ToString();
            return Timeline.Parse(Connection.OAuthWebRequest(TwitterConnection.Method.GET, url), this);
        }

        public Timeline GetPublicTimeline()
        {
            string url = "public_timeline.xml";
            return GetTimeline(url, new TimelineOption());
        }

        public Timeline GetHomeTimeline(TimelineOption option)
        {
            string url = "home_timeline.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetHomeTimeline()
        {
            return GetHomeTimeline(new TimelineOption());
        }

        public Timeline GetFriendsTimeline(TimelineOption option)
        {
            string url = "friends_timeline.xml";
            return GetTimeline(url, option);
        }
        public Timeline GetFriendsTimeline()
        {
            return GetFriendsTimeline(new TimelineOption());
        }

        public Timeline GetUserTimeline(TimelineOption option)
        {
            string url = "user_timeline.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetUserTimeline()
        {
            return GetUserTimeline(new TimelineOption());
        }

        public Timeline GetRepliesTimeline(TimelineOption option)
        {
            string url = "replies.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetRepliesTimeline()
        {
            return GetRepliesTimeline(new TimelineOption());
        }

        public Timeline GetMentionsTimeline(TimelineOption option)
        {
            string url = "mentions.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetMentionsTimeline()
        {
            return GetMentionsTimeline(new TimelineOption());
        }

        public Timeline GetRetweetedByMeTimeline(TimelineOption option)
        {
            string url = "retweeted_by_me.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetRetweetedByMeTimeline()
        {
            return GetRetweetedByMeTimeline(new TimelineOption());
        }

        public Timeline GetRetweetedToMeTimeline(TimelineOption option)
        {
            string url = "retweeted_to_me.xml";
            return GetTimeline(url, option);
        }

        public Timeline GetRetweetedToMeTimeline()
        {
            return GetRetweetedToMeTimeline(new TimelineOption());
        }

        public Timeline GetRetweetedOfMeTimeline(TimelineOption option)
        {
            string url = "retweets_of_me.xml";
            return GetTimeline(url, option);
        }
        public Timeline GetRetweetedOfMeTimeline()
        {
            return GetRetweetedOfMeTimeline(new TimelineOption());
        }

        #endregion

        #region Tweets

        protected Timeline PostTimeline(string name, string postData)
        {
            Uri uri = new Uri(apiUri, "statuses/" + name);
            return Timeline.Parse(Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), postData), this);
        }

        public Status Show(Status status)
        {
            Uri uri = new Uri(apiUri, "statuses/show.xml");
            string query = "id=" + status.StatusID.ToString();
            return new Status().Parse(Connection.OAuthWebRequest(uri.ToString(), query), this);
        }

        public Status ShowStatus(long statusID)
        {
            return Show(new Status() { StatusID = statusID });
        }

        public Timeline Update(string statusText)
        {
            string name = "update.xml";
            string query = string.Format("status={0}", Uri.EscapeUriString(statusText));
            return PostTimeline(name, query);
        }

        public Timeline Update(string statusText, Status inReplyToStatus)
        {
            string name = "update.xml";
            string query = string.Format("status={0}&in_reply_to_status_id={1}", Uri.EscapeUriString(statusText), inReplyToStatus.StatusID);
            return PostTimeline(name, query);
        }

        public Timeline Update(string status, long inReplyToStatusID)
        {
            return Update(status, new Status() { StatusID = inReplyToStatusID });
        }

        public Timeline Destroy(Status status)
        {
            string name = "destroy.xml";
            string query = "id=" + status.StatusID.ToString();
            return PostTimeline(name, query);
        }

        public Timeline DestroyStatus(long statusID)
        {
            return Destroy(new Status() { StatusID = statusID });
        }

        public Timeline Retweet(Status status)
        {
            return PostTimeline(string.Format("retweet/{0}.xml", status.StatusID), "");
        }

        public Timeline Retweet(long statusID)
        {
            return Retweet(new Status() { StatusID = statusID });
        }

        /*
        public Timeline Retweets(long statusID, int count)
        {
            return PostTimeline(string.Format("retweets/{0}.xml", statusID), "count=" + count.ToString());
        }*/

        #endregion

        #region Users

        /*
        public User VerifyCredentials()
        {
            string url = "http://twitter.com/account/verify_credentials.xml";
            string result = Connection.OAuthWebRequest(TwitterConnection.Method.GET, url);
            return User.Parse(result);
        }
        */

        public User Show(User user)
        {
            Uri uri = new Uri(apiUri, "users/show.xml");
            string query = GetUserQueryString(user);
            return new User().Parse(Connection.OAuthWebRequest(uri.ToString(), query));
        }

        public User ShowUser(long userID)
        {
            return Show(new User() { UserID = userID });
        }

        public User ShowUser(string screenName)
        {
            return Show(new User() { ScreenName = screenName });
        }

        public User Lookup(User user)
        {
            Uri uri = new Uri(apiUri, "users/lookup.xml");
            string query = GetUserQueryString(user);
            return new User().Parse(Connection.OAuthWebRequest(uri.ToString(), query));
        }


        public UserList Lookup(User[] users)
        {
            Uri uri = new Uri(apiUri, "users/lookup.xml");
            string query = GetUsersQueryString(users);
            return UserList.ParseAll(Connection.OAuthWebRequest(uri.ToString(), query));
        }

        public UserList SearchUser(string keyword, int count, int page)
        {
            Uri uri = new Uri(apiUri, "users/search.xml");
            string query = "q=" + keyword;
            return UserList.ParseAll(Connection.OAuthWebRequest(uri.ToString(), query));
        }

        public UserList SearchUser(string keyword, int count)
        {
            return SearchUser(keyword, count, 0);
        }

        public UserList SearchUser(string keyword)
        {
            return SearchUser(keyword, 0, 0);
        }


        public UserList Friends(User user)
        {
            UserListOption option = new UserListOption();
            option.User = user;
            return Friends(option);
        }

        public UserList Friends(UserListOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("statuses/friends.xml{0}", option.ToString()));
            return UserList.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }

        public UserList Followers(User user)
        {
            UserListOption option = new UserListOption();
            option.User = user;
            return Friends(option);
        }

        public UserList Followers(UserListOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("statuses/followers.xml{0}", option.ToString()));
            return UserList.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }

        #endregion

        #region Trends
        #endregion

        #region List

        public TwitterList CreateList(ListCreateOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists.xml", this.CurrentUser.ScreenName));
            string query = option.CreateQueryString();
            return new TwitterList().Parse(Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query));
        }

        public TwitterList UpdateList(TwitterList list)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/{1}.xml", this.CurrentUser.ScreenName, list.TwitterListID));
            var option = new ListCreateOption()
            {
                name = list.Name,
                mode = (ListMode)Enum.Parse(typeof(ListMode), list.Mode),
                description = list.Description,
            };
            var query = option.CreateQueryString();
            return new TwitterList().Parse(Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query));
        }

        //private string SetCursor(string resultData, ref ListsGetOption option)
        //{
        //    var e = XElement.Parse(resultData);
        //    option.next_cursor = long.Parse(e.Element("next_cursor").Value);
        //    option.previous_cursor = long.Parse(e.Element("previous_cursor").Value);

        //    return resultData;
        //}



        public TwitterListCollection GetLists(User user, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists.xml{1}", user.ScreenName, option.ToString()));
            return TwitterListCollection.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }

        public TwitterList GetList(TwitterList list)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/{1}.xml", list.OwnerUser.ScreenName, list.TwitterListID));
            return new TwitterList().Parse(Connection.OAuthWebRequest(TwitterConnection.Method.GET, uri.ToString()));
        }

        public void DeleteList(TwitterList list)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/{1}.xml", list.OwnerUser.ScreenName, list.Name));
            string postData = "_method=DELETE";

            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), postData);
        }

        public Timeline GetListTimeline(TwitterList list, ListTimelineOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/{1}/statuses.xml", list.OwnerUser.ScreenName, list.TwitterListID));
            return Timeline.Parse(Connection.OAuthWebRequest(TwitterConnection.Method.GET, uri.ToString() + option.ToString()), this);
        }

        #endregion

        #region List Members

        public TwitterListCollection GetListsOfMembership(User user, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/memberships.xml{1}", user.ScreenName, option.ToString()));
            return TwitterListCollection.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }

        public UserList GetMembersOfList(TwitterList list, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/{1}/members.xml", list.OwnerUser.ScreenName, list.TwitterListID));
            return UserList.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }

        public TwitterList AddMember(User user, TwitterList list, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/{1}/members.xml", list.OwnerUser.ScreenName, list.TwitterListID));
            string postData = string.Format("id={0}", user.UserID);

            var responseData = Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), postData);
            return new TwitterList().Parse(responseData);
        }

        public TwitterList DeleteMember(User user, TwitterList list, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/{1}/members.xml", list.OwnerUser.ScreenName, list.TwitterListID));
            string postData = string.Format("_method=DELETE&id={0}", user.UserID);

            var responseData = Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), postData);
            return new TwitterList().Parse(responseData);
        }

        public User ExistsMember(User user, TwitterList list)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/{1}/members/{1}.xml", list.OwnerUser.ScreenName, list.TwitterListID, user.UserID));
            try
            {
                return new User().Parse(Connection.OAuthWebRequest(uri.ToString()));
            }
            catch
            {
                return null;
            }
        }

        #endregion

        #region List Subscrivers

        public TwitterListCollection GetListsOfSubscriptions(User user, ListsGetOption option)
        {
            Uri uri = new Uri(apiUri, string.Format("{0}/lists/subscriptions.xml{1}", user.ScreenName, option.ToString()));
            return TwitterListCollection.ParseAll(Connection.OAuthWebRequest(uri.ToString()));
        }


        #endregion

        #region Direct Messages

        public DirectMessageCollection GetDirectMessages(DirectMessageOption option)
        {
            Uri messageUri = new Uri(apiUri, "direct_messages.xml");
            string url = messageUri.ToString() + option.ToString();
            return DirectMessageCollection.ParseAll(Connection.OAuthWebRequest(TwitterConnection.Method.GET, url));
        }

        public DirectMessageCollection GetDirectMessages()
        {
            return GetDirectMessages(new DirectMessageOption());
        }

        public DirectMessageCollection GetSentDirectMessages(DirectMessageOption option)
        {
            Uri messageUri = new Uri(apiUri, "direct_messages/sent.xml");
            string url = messageUri.ToString() + option.ToString();
            return DirectMessageCollection.ParseAll(Connection.OAuthWebRequest(TwitterConnection.Method.GET, url));
        }

        public DirectMessageCollection GetSentDirectMessages()
        {
            return GetSentDirectMessages(new DirectMessageOption());
        }

        public void NewDirectMessage(User user, string message)
        {
            Uri messageUri = new Uri(apiUri, "direct_messages/new.xml");
            string url = messageUri.ToString();
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, url, string.Format("{0}&text={1}", GetUserQueryString(user), Uri.EscapeUriString(message)));
        }

        public void NewDirectMessage(string screenName, string message)
        {
            NewDirectMessage(new User() { ScreenName = screenName }, message);
        }

        public void NewDirectMessage(long userID, string message)
        {
            NewDirectMessage(new User() { UserID = userID }, message);
        }

        public void Destroy(DirectMessage directMessage)
        {
            Uri messageUri = new Uri(apiUri, "direct_messages/destroy.xml");
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, messageUri.ToString(), "id=" + directMessage.DirectMessageID.ToString());
        }

        public void DestroyDirectMessage(long directMessageID)
        {
            Uri messageUri = new Uri(apiUri, "direct_messages/destroy.xml");
            string url = messageUri.ToString() + "?id=" + directMessageID.ToString();
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, url);
        }

        #endregion

        #region Friendships

        public User CreateFriendShip(User user, bool follow)
        {
            Uri uri = new Uri(apiUri, string.Format("friendships/create/{0}.xml?follow={1}", GetUserID(user), follow.ToString()));
            return new User().Parse(Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString()));
        }

        public void DestroyFriendShip(User user)
        {
            Uri uri = new Uri(apiUri, "friendships/destroy.xml");
            string query = GetUserQueryString(user);
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query);
        }

        /*
        public void ExistFriendShip(User user1, User user2)
        {
         
        }
        */


        #endregion

        #region Friends and Followers
        #endregion

        #region  Account

        public User VerifyCredentials()
        {
            Uri uri = new Uri(apiUri, "account/verify_credentials.xml");
            this.CurrentUser = new User().Parse(Connection.OAuthWebRequest(uri.ToString()));
            return this.CurrentUser;
        }
        public RateLimit RateLimitStatus()
        {
            Uri uri = new Uri(apiUri, "account/rate_limit_status.xml");
            return RateLimit.Parse(Connection.OAuthWebRequest(uri.ToString()));
        }
        /*
        public void EndSession()
        {
            Uri uri = new Uri(apiUri, "account/end_session");
        }
        public void UpdateDeliveryDevice()
        {
            Uri uri = new Uri(apiUri, "account/update_delivary_device");
        }
        public void UpdateProfile_colors()
        {
            Uri uri = new Uri(apiUri, "account/update_profile_colors");
        }

        public void UpdateProfile_image()
        {
            Uri uri = new Uri(apiUri, "account/update_profile_image");
        }
        public void UpdateProfile_background_image()
        {
            Uri uri = new Uri(apiUri, "account/update_profile_background_image");
        }
        public void UpdateProfile()
        {
            Uri uri = new Uri(apiUri, "account/update_profile");
        }
        */

        #endregion

        #region Favorites

        public Timeline Favorites(User user, int page)
        {
            Uri uri = new Uri(apiUri, "favorites.xml");
            string query = GetUserQueryString(user);
            if (page > 0)
            {
                query += "&page=" + page.ToString();
            }
            return Timeline.Parse(Connection.OAuthWebRequest(uri.ToString(), query), this);
        }

        public Timeline Favorites(User user)
        {
            return Favorites(user, 0);
        }

        public Timeline Favorites(long userID)
        {
            return Favorites(new User() { UserID = userID });
        }

        public void CreateFavorites(Status status)
        {
            Uri uri = new Uri(apiUri, string.Format("favorites/create/{0}.xml", status.StatusID));
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString());
        }

        public void CreateFavorites(long statusID)
        {
            CreateFavorites(new Status() { StatusID = statusID });
        }

        public void DestroyFavorites(Status status)
        {
            Uri uri = new Uri(apiUri, "favorites/destroy.xml");
            string query = "id=" + status.StatusID;
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query);
        }

        #endregion

        #region Notification
        #endregion

        #region Block

        private void CreateBlock(User user)
        {
            Uri uri = new Uri(apiUri, "blocks/create.xml");
            string query = GetUserQueryString(user);
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query);
        }

        private void DestroyBlock(User user)
        {
            Uri uri = new Uri(apiUri, "blocks/destroy.xml");
            string query = GetUserQueryString(user);
            Connection.OAuthWebRequest(TwitterConnection.Method.POST, uri.ToString(), query);
        }

        #endregion

        #region Spam Reporting
        #endregion

        #region Saved Searches
        #endregion

        #region OAuth
        #endregion

        #region Local Trends
        #endregion

        #region Geo
        #endregion

        #region Streaming

        public TwitterStreamMonitor OpenUserStreams()
        {
            Uri uri = new Uri(streamUri, "user.json");
            var monitor = new TwitterStreamMonitor(this.Connection.TwitterStreamRequest(uri.ToString()), this.Connection, uri);
            
            return monitor;
        }

        #endregion

        #region Help

        private bool Test()
        {
            Uri uri = new Uri("http://twitter.com/help/text.xml");
            string response = Connection.OAuthWebRequest(uri.ToString());
            return response.Contains("ok");
        }

        public bool TestStreaming()
        {
            var monitor = this.OpenUserStreams();
            var tsk = System.Threading.Tasks.Task.Factory.StartNew(() => monitor.StartMonitoring());

            System.Threading.Thread.Sleep(360 * 1000);
            monitor.Dispose();
            return true;
        }

        #endregion

        #region Private Uri

        private Uri apiUri;
        private Uri streamUri;

        #endregion

        #region Private Const

        private const double version = 1.0;
        private const double streamVersion = 2.0;

        #endregion

        #region Private Methods

        private string GetUserQueryString(User user)
        {
            string query = "";
            if (user.UserID > 0)
            {
                query = "user_id=" + user.UserID.ToString();
            }
            else if (!string.IsNullOrEmpty(user.ScreenName))
            {
                query = "screen_name=" + user.ScreenName;
            }
            return query;
        }

        private string GetUsersQueryString(User[] users)
        {
            string query = "";
            if (users.Length > 0)
            {
                if (users[0].UserID > 0)
                {
                    query = "user_id=";
                    foreach (User user in users)
                    {
                        query += user.UserID;
                        query += ",";
                    }
                }
                else if (users[0].ScreenName.Length > 0)
                {
                    query = "screen_name=";

                    foreach (User user in users)
                    {
                        query += user.ScreenName;
                        query += ",";
                    }
                }
            }
            if (query.EndsWith(","))
            {
                query = query.Substring(0, query.Length - 1);
            }
            return query;
        }

        private string GetUserID(User user)
        {
            string result = "";
            if (user.UserID > 0)
            {
                result = user.UserID.ToString();
            }
            else if (!string.IsNullOrEmpty(user.ScreenName))
            {
                result = user.ScreenName;
            }
            return result;
        }

        #endregion
    }
}
